package edu.mvcframework.servlet;

import com.wr.edu.mvcframework.annotations.*;
import com.wr.edu.mvcframework.pojo.Handler;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 王瑞
 * @description:
 * @date 2020/2/20  22:40
 */
public class Myservlet extends HttpServlet {

    private Properties properties = new Properties();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        Handler handler = getHandler(req);
        //获取当前用户名
        String username = req.getParameter("username");
        //设置response的编码格式
        resp.setContentType("text/html;charset=UTF-8");
        if (handler!=null){
            //判断当前用户是否拥有访问权限
            Boolean hasSecurity = isHasSecurity(handler, username);
            //如果拥有访问权限 执行赋值和handler逻辑
            if (hasSecurity){
                //设置参数
                Object[] params = setParam(handler, req, resp);
                Method method = handler.getMethod();
                try {
                    method.invoke(handler.getCotrollerObj(), params);
                    resp.getWriter().print("用户："+username+"访问成功");

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                //如果不符合 则在页面提示输出
            }else {

                resp.getWriter().print("对不起！当前用户：'"+username+"'没有"+req.getRequestURI()+"的访问权限");
            }

        }



    }

    //此方法为Method设置一个参数数组
    public Object[] setParam(Handler handler,HttpServletRequest req, HttpServletResponse resp){
        Method method = handler.getMethod();
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] params = new Object[parameterTypes.length];

        Map<String, String[]> parameterMap = req.getParameterMap();
        Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
        Map<String, Integer> paramIndexMapping = handler.getParamIndexMapping();
        for (Map.Entry<String, String[]> entry : entries) {
            String name = entry.getKey();
            String[] value = entry.getValue();
            String join = StringUtils.join(value, ",");
            boolean b = paramIndexMapping.containsKey(name);
            if (b) {
                Integer index = paramIndexMapping.get(name);
                params[index] = join;
            }
        }
        Integer index = paramIndexMapping.get(HttpServletRequest.class.getName());
        Integer index1 = paramIndexMapping.get(HttpServletResponse.class.getName());
        if (index!=null){
            params[index] = req;
        }
        if (index!=null){
            params[index1] = resp;
        }

        return params;
    }

    //此方法用来判断当前的访问用户是否拥有该handler的访问权限
    public Boolean isHasSecurity(Handler handler,String username){
        //定义一个变量 用于判断当前用户是否有权限
        Boolean has = false;
        //获得当前用户名

        //获得当前method对象
        Method method = handler.getMethod();
        //判断当前方法是否被@Security注解标记过
        boolean methodAnnotationPresent = method.isAnnotationPresent(Security.class);
        if (methodAnnotationPresent){
            //如果被@Security标记过 获得这个注解内的参数
            Security annotation = method.getAnnotation(Security.class);
            //获得注解中的参数
            String[] values = annotation.value();
            //判断Security的参数是否包含当前用户  如果包含 直接返回true
            for (String value : values) {
                if (!value.equals(username)){continue;}
                has = true;
                return has;
            }
        }else {
            //如果方法上没有标记@Security 则在类上查找这个注解
            Object cotrollerObj = handler.getCotrollerObj();
            Class<?> aClass = cotrollerObj.getClass();
            boolean annotationPresent = aClass.isAnnotationPresent(Security.class);
            //判断当前类是否标记了@Security
            if (annotationPresent){
                //如果标记了 且vulue值包含当前用户 则表示当前类的所有方法都可以被这个用户访问    返回true
                Security annotation = aClass.getAnnotation(Security.class);
                String[] values = annotation.value();
                for (String value : values) {
                    if (!value.equals(username)){continue;}
                    has = true;
                }
            }

        }


        return has;
    }

    private Handler getHandler(HttpServletRequest req) {
        if (handlerMapping.isEmpty()) {
            return null;
        }

        String requestURI = req.getRequestURI();
        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.getPattern().matcher(requestURI);
            if (!matcher.matches()) {
                continue;
            }
            return handler;
        }
        return null;

    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        System.out.println("需要扫描的包名： " + contextConfigLocation);

        //加载配置文件
        doLoadconfig(contextConfigLocation);

        Object scanPackage = properties.get("scanPackage");

        //扫描相关的类，扫描注解
        doScan(scanPackage.toString());

        //初始化bean对象
        diInstance();

        //实现依赖注入
        doAutowired();

        //配置一个HandlerMapping处理器映射器，讲url和Method建立关系
        initHandlerMapping();


        System.out.println("servlet初始化完成");
    }

    //Map<String,Object> handlerMapping = new HashMap<>();
    private List<Handler> handlerMapping = new ArrayList<>();

    private void initHandlerMapping() {

        if (ioc.size() == 0) {
            return;
        }

        Set<Map.Entry<String, Object>> entries = ioc.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            Object bean = entry.getValue();
            Class<?> aClass = bean.getClass();
            if (!aClass.isAnnotationPresent(MyController.class)) {
                continue;
            }
            MyRequestMapping annotation1 = aClass.getAnnotation(MyRequestMapping.class);
            String value1 = annotation1.value();
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                String value = annotation.value();
                Handler handler = new Handler(bean, method, Pattern.compile(value1 + value));
                Map<String, Integer> paramIndexMapping = handler.getParamIndexMapping();
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    Class<?> type = parameter.getType();
                    if (type == HttpServletRequest.class) {
                        paramIndexMapping.put(HttpServletRequest.class.getSimpleName(), i);
                    } else if (type == HttpServletResponse.class) {
                        paramIndexMapping.put(HttpServletResponse.class.getSimpleName(), i);
                    } else {
                        paramIndexMapping.put(parameter.getName(), i);
                    }
                }
                handler.setParamIndexMapping(paramIndexMapping);

                handlerMapping.add(handler);
            }
        }

    }

    private void doAutowired() {

        if (ioc.size() == 0) {
            return;
        }
        Set<Map.Entry<String, Object>> entries = ioc.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            Object value = entry.getValue();
            Field[] fields = value.getClass().getFields();
            for (Field field : fields) {
                boolean annotationPresent = field.isAnnotationPresent(MyAutowired.class);
                if (!annotationPresent) {
                    continue;
                }
                MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                String beanName = annotation.value();
                if ("".equals(beanName.trim())) {
                    beanName = field.getType().getName();
                }
                Object o = ioc.get(beanName);

                field.setAccessible(true);
                try {
                    field.set(value, o);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }

    }

    private Map<String, Object> ioc = new HashMap<>();

    private void diInstance() {

        try {
            for (String className : classNameList) {
                Class<?> aClass = Class.forName(className);
                boolean annotationPresent = aClass.isAnnotationPresent(MyController.class);
                String s = lowerFirst(aClass.getSimpleName());
                if (annotationPresent) {
                    Object o = aClass.newInstance();
                    ioc.put(s, o);
                } else if (aClass.isAnnotationPresent(MyService.class)) {
                    MyService annotation = aClass.getAnnotation(MyService.class);
                    String value = annotation.value();
                    if (!"".equals(value)) {
                        ioc.put(value, aClass.newInstance());
                    } else {
                        ioc.put(s, aClass.newInstance());
                    }
                }

                Class<?>[] interfaces = aClass.getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    String name = anInterface.getName();
                    ioc.put(name, aClass.newInstance());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public String lowerFirst(String str) {

        String substring = str.substring(0, 1).toLowerCase();

        String substring1 = str.substring(1, str.length());


        return substring + substring1;


    }

    private List<String> classNameList = new ArrayList<>();

    private void doScan(String packageName) {
        //拿到项目的根目录
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        //获得这个类在实际硬盘上的路径  方便创建文件对象
        String apath = path + packageName.replaceAll("\\.", "/");
        File file = new File(apath);
        //获得子文件集合
        File[] files = file.listFiles();
        for (File file1 : files) {
            //获得文件名称
            String name = file1.getName();
            //如果是目录 就继续递归
            if (file1.isDirectory()) {
                doScan(packageName + "." + name);
                //如果不是目录 且以.class结尾 就获取这个文件的名称并去掉.class后缀  至此拿到类的全限定名称
            } else if (name.endsWith(".class")) {
                String s = name.replaceAll(".class", "");
                classNameList.add(packageName + "." + s);
            }
        }

    }

    private void doLoadconfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            this.properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
